﻿using NUnit.Framework;
using System;
using System.Collections.Generic;
using System.Text;
using DotNetCommon.Extensions;
using Shouldly;
using DotNetCommon.Data;
using System.Linq;

namespace DotNetCommon.Test.Extensions
{
    /// <summary>
    /// 测试Object.MapperModify()方法
    /// </summary>
    [TestFixture]
    public class ObjectTests_MapperModify
    {
        #region 转换示例
        [Test]
        public void MapperWithBaseObj()
        {
            var stu = new StudentEntity()
            {
                Id = 1,
                Name = "小明",
                Birth = DateTime.Parse("1988-01-01"),
                Nicks = new List<string>() { "小张", "小明" },
                Score = 98.2,
                Books = new List<Book>()
                {
                    new Book()
                    {
                        Id=1,
                        Name="语文"
                    },
                    new Book()
                    {
                        Id=2,
                        Name="数学"
                    }
                },
                Manage = new StudentEntity()
                {
                    Id = 2,
                    Name = "小花",
                    Score = 99,
                    Birth = DateTime.Parse("2000-02-01"),
                    Nicks = new List<string>() { "小花", "xiaohua" }
                }
            };
            var stuReq = new StudentReq()
            {
                Id = 1,
                Name = "小明2",
                Score = 85,
                Nicks = new List<string>() { "小明2" },
                Manage = new StudentReq()
                {
                    Id = 3,
                    Name = "小刚",
                    Score = 90
                }
            };
            var ent = stu.ModifyByDto(stuReq);

            int i = 0;
            ent.ShouldNotBeNull();
            ent.Id.ShouldBe(1);
            ent.Name.ShouldBe("小明2");
            ent.Nicks.ShouldNotBeNull();
            ent.Nicks.Count.ShouldBe(1);
            ent.Nicks.ShouldBe(new List<string>() { "小明2" });
            ent.Manage.ShouldNotBeNull();
            ent.Manage.Id.ShouldBe(3);
            ent.Manage.Name.ShouldBe("小刚");
            ent.Manage.Score.ShouldBe(90);

            ent.Birth.ShouldBe(DateTime.Parse("1988-01-01"));
            ent.Books.ShouldNotBeNull();
            ent.Books.Count.ShouldBe(2);
            ent.Books[0].Name.ShouldBe("语文");
            ent.Books[1].Name.ShouldBe("数学");

            ent.Manage.Birth.ShouldBe(DateTime.Parse("2000-02-01"));
            ent.Manage.Nicks.ShouldBeNull();
            ent.Manage.Books.ShouldBeNull();
            ent.Manage.Manage.ShouldBeNull();


        }
        #endregion

        #region 数据类型
        public class ModelEntity
        {
            public int PInt { get; set; }
            public int? PIntNull { get; set; }
            public long PLong { get; set; }
            public long? PLongNull { get; set; }
            public bool PBool { get; set; }
            public bool? PBoolNull { get; set; }

            public DateTime PDateTime { get; set; }
            public DateTime? PDateTimeNull { get; set; }

            public string PString { get; set; }
            public string PString2 { get; set; }

            public EnumTest PEnum { get; set; }
            public EnumTest? PEnumNull { get; set; }

            public enum EnumTest
            {
                Open, Close, Dispose
            }
        }

        [Test]
        public void TestPropType()
        {
            var ent = new ModelEntity()
            {
                PInt = 1,
                PIntNull = 12,
                PLong = 100,
                PLongNull = 2,
                PBool = true,
                PBoolNull = true,
                PDateTime = new DateTime(2020, 01, 01),
                PDateTimeNull = new DateTime(2001, 01, 02),
                PEnum = ModelEntity.EnumTest.Dispose,
                PEnumNull = ModelEntity.EnumTest.Close,
                PString = "小明",
                PString2 = "小明Null"
            };
            ent.ModifyByDto(new
            {
                PInt = 11,
                PIntNull = 121,
                PLong = 1000L,
                PLongNull = 21L,
                PBool = false,
                PDateTime = new DateTime(2020, 11, 11),
                PEnum = ModelEntity.EnumTest.Close,
                PString = "小明2",
            });

            ent.ShouldNotBeNull();
            ent.PInt.ShouldBe(11);
            ent.PIntNull.ShouldBe(121);
            ent.PLong.ShouldBe(1000);
            ent.PLongNull.ShouldBe(21);
            ent.PBool.ShouldBe(false);
            ent.PBoolNull.ShouldBe(true);
            ent.PDateTime.ShouldBe(new DateTime(2020, 11, 11));
            ent.PDateTimeNull.ShouldBe(new DateTime(2001, 01, 02));
            ent.PEnum.ShouldBe(ModelEntity.EnumTest.Close);
            ent.PEnumNull.ShouldBe(ModelEntity.EnumTest.Close);
            ent.PString.ShouldBe("小明2");
            ent.PString2.ShouldBe("小明Null");
        }

        #endregion

        #region 集合的直接覆盖
        public class Model
        {
            public int Id { get; set; }
            public string Name { get; set; }
        }
        public class ListEntity
        {
            public int Id { get; set; }
            public string Name { get; set; }

            public int[] Ages { get; set; }
            public List<int> Ages2 { get; set; }
            public IEnumerable<int> Ages3 { get; set; }
            public ICollection<int> Ages4 { get; set; }

            public string[] Names { get; set; }
            public List<string> Names2 { get; set; }
            public IEnumerable<string> Names3 { get; set; }
            public ICollection<string> Names4 { get; set; }

            public Model[] Models { get; set; }
            public List<Model> Models2 { get; set; }
            public IEnumerable<Model> Models3 { get; set; }
            public ICollection<Model> Models4 { get; set; }

        }

        [Test]
        public void TestCollection()
        {
            var ent = new ListEntity()
            {
                Id = 1,
                Name = "小明",
                Ages = new int[] { 1, 2 },
                Ages2 = new List<int>() { 1, 2 },
                Ages3 = new List<int>() { 1, 2 },
                Ages4 = new List<int>() { 1, 2 },

                Names = new string[] { "a", "b" },
                Names2 = new List<string>() { "a", "b" },
                Names3 = new List<string>() { "a", "b" },
                Names4 = new List<string>() { "a", "b" },

                Models = new Model[] { new Model() { Id = 1, Name = "hah" } },
                Models2 = new List<Model>() { },
                Models3 = new List<Model>() { new Model() { Id = 1, Name = "小明" } },
                Models4 = new List<Model>() { new Model() { } }
            };
            //不做任何修改
            ent.ModifyByDto(new { });
            ent.Id.ShouldBe(1);
            ent.Name.ShouldBe("小明");
            ent.Ages.ShouldBe(new int[] { 1, 2 });
            ent.Ages2.ShouldBe(new List<int>() { 1, 2 });
            ent.Ages3.ShouldBe(new List<int>() { 1, 2 });
            ent.Ages4.ShouldBe(new List<int>() { 1, 2 });
            ent.Names.ShouldBe(new string[] { "a", "b" });
            ent.Names2.ShouldBe(new List<string>() { "a", "b" });
            ent.Names3.ShouldBe(new List<string>() { "a", "b" });
            ent.Names4.ShouldBe(new List<string>() { "a", "b" });

            ent.Models.Length.ShouldBe(1); ent.Models[0].Name.ShouldBe("hah");
            ent.Models2.Count.ShouldBe(0);
            ent.Models3.ToList().Count.ShouldBe(1); ent.Models3.ToList()[0].Name.ShouldBe("小明");
            ent.Models4.Count.ShouldBe(1); ent.Models4.ToList()[0].Name.ShouldBe(null);

            //针对集合属性的修改将直接替换整个属性
            ent.ModifyByDto(new
            {
                Id = 1,
                Ages = new int[0],
                Ages2 = new List<int>() { 1, 3 },
                Names = new string[] { "c", "b" },
                Names3 = new List<string>() { },
                Models = new Model[1],
                Models4 = new List<Model>() { new Model() { Id = 5 } }
            });
            ent.Id.ShouldBe(1);
            ent.Name.ShouldBe("小明");
            ent.Ages.ShouldBe(new int[0]);
            ent.Ages2.ShouldBe(new List<int>() { 1, 3 });
            ent.Ages3.ShouldBe(new List<int>() { 1, 2 });
            ent.Ages4.ShouldBe(new List<int>() { 1, 2 });
            ent.Names.ShouldBe(new string[] { "c", "b" });
            ent.Names2.ShouldBe(new List<string>() { "a", "b" });
            ent.Names3.ShouldBe(new List<string>());
            ent.Names4.ShouldBe(new List<string>() { "a", "b" });

            ent.Models.Length.ShouldBe(1); ent.Models[0].ShouldBeNull();
            ent.Models2.Count.ShouldBe(0);
            ent.Models3.ToList().Count.ShouldBe(1); ent.Models3.ToList()[0].Name.ShouldBe("小明");
            ent.Models4.Count.ShouldBe(1); ent.Models4.ToList()[0].Name.ShouldBe(null); ent.Models4.ToList()[0].Id.ShouldBe(5);

        }
        #endregion

        #region 三层递归
        public class ModelA
        {
            public int Id { get; set; }
            public string Name { get; set; }
            public ModelB B { get; set; }
        }
        public class ModelB
        {
            public int Id { get; set; }
            public string Name { get; set; }
            public ModelC C { get; set; }
        }

        public class ModelC
        {
            public int Id { get; set; }
            public string Name { get; set; }
        }

        [Test]
        public void TestRecure()
        {
            var ent = new ModelA()
            {
                Id = 1,
                Name = "ModelA",
                B = new ModelB()
                {
                    Id = 2,
                    Name = "ModelB",
                    C = new ModelC()
                    {
                        Id = 3,
                        Name = "ModelC"
                    }
                }
            };
            ent.ModifyByDto(new
            {
                Id = 2,
                Name = "ModelA2",
                B = new
                {
                    Id = 3,
                    C = new
                    {
                        Id = 4
                    }
                }
            });
            ent.Id.ShouldBe(2);
            ent.Name.ShouldBe("ModelA2");
            ent.B.ShouldNotBe(null);
            ent.B.Id.ShouldBe(3);
            ent.B.Name.ShouldBe("ModelB");
            ent.B.C.ShouldNotBe(null);
            ent.B.C.Id.ShouldBe(4);
            ent.B.C.Name.ShouldBe("ModelC");
        }
        #endregion

        #region 保留循环引用关系
        public class CircleEntity
        {
            public int Id { get; set; }
            public PersonCircle Person { get; set; }
            public BookCircle Book { get; set; }
        }

        public class CircleDto
        {
            public int Id { get; set; }
            public PersonCircleDto Person { get; set; }
            public BookCircleDto Book { get; set; }
        }
        [Test]
        public void TestCircle()
        {
            var ent = new CircleEntity()
            {
                Id = 1,
                Person = new PersonCircle()
                {
                    Id = 1,
                    Name = "小明",
                    Pwd = "xiaopming",
                    Book = new BookCircle()
                    {
                        Id = 1,
                        Name = "语文"
                    }
                }
            };
            ent.Book = ent.Person.Book;
            ent.Book.Person = ent.Person;

            var dto = new CircleDto()
            {
                Id = 2,
                Person = new PersonCircleDto()
                {
                    Id = 2,
                    Name = "小明2",
                    Book = new BookCircleDto()
                    {
                        Id = 3,
                        Name = "语文3"
                    }
                }
            };
            dto.Book = dto.Person.Book;
            dto.Book.Person = dto.Person;
            ent.ModifyByDto(dto);
            //先检测正常属性改变情况
            ent.Id.ShouldBe(2);
            ent.Person.ShouldNotBeNull();
            ent.Person.Id.ShouldBe(2);
            ent.Person.Name.ShouldBe("小明2");
            ent.Person.Pwd.ShouldBe("xiaopming");
            ent.Person.Book.ShouldNotBeNull();
            ent.Person.Book.Id.ShouldBe(3);
            ent.Person.Book.Name.ShouldBe("语文3");
            ent.Book.ShouldNotBeNull();
            //再检测循环引用关系是否保留
            ent.Book.ShouldBe(ent.Person.Book);

            //dto中没有循环引用关系
            var dto2 = new
            {
                Id = 2,
                Person = new
                {
                    Id = 2,
                    Name = "小明2",
                    Book = new
                    {
                        Id = 3,
                        Name = "语文3"
                    }
                }
            };
            ent.ModifyByDto(dto2);
            //先检测正常属性改变情况
            ent.Id.ShouldBe(2);
            ent.Person.ShouldNotBeNull();
            ent.Person.Id.ShouldBe(2);
            ent.Person.Name.ShouldBe("小明2");
            ent.Person.Pwd.ShouldBe("xiaopming");
            ent.Person.Book.ShouldNotBeNull();
            ent.Person.Book.Id.ShouldBe(3);
            ent.Person.Book.Name.ShouldBe("语文3");
            ent.Book.ShouldNotBeNull();
            //再检测循环引用关系是否保留
            ent.Book.ShouldBe(ent.Person.Book);

        }
        #endregion
    }

    #region 转换示例

    public class StudentEntity
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public DateTime Birth { get; set; }
        public double Score { get; set; }

        public List<string> Nicks { get; set; }

        public List<Book> Books { get; set; }

        public StudentEntity Manage { get; set; }
    }

    public class Book
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }

    public class StudentReq
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public double Score { get; set; }
        public List<string> Nicks { get; set; }

        public StudentReq Manage { get; set; }
    }

    public class StudentRes
    {

    }

    #endregion
}
